#include "SceneManager.h"


SceneManager::SceneManager(void)
	:m_pDevice(NULL)
	,m_pSceneRootNode(NULL)
{
}


SceneManager::~SceneManager(void)
{
	DestroyNode(m_pSceneRootNode);
}

void SceneManager::Init( ID3D10Device* device )
{
	m_pDevice = device;

	m_pSceneRootNode = new SceneNode();
	m_pSceneRootNode->SetNodeName("SceneRoot");

	m_ParalleLightEffect.Init(m_pDevice);
}

void SceneManager::RenderOneFrame()
{
	m_pDevice->IASetInputLayout(m_ParalleLightEffect.GetInputLayout());
	m_pDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	//set light and eye position
	Camera* pCamera = static_cast<Camera*>(GetObjectByName("MainCamera"));

	m_ParalleLightEffect.SetEyePos(pCamera->GetPosition());

	//Light light;
	////light.dir      = D3DXVECTOR3(0.707f, 0.0f, -0.707f);
	//light.dir      = D3DXVECTOR3(0.57735f, -0.57735f, 0.57735f);
	//light.ambient  = D3DXCOLOR(0.3f, 0.3f, 0.3f, 1.0f);
	//light.diffuse  = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
	////light.specular = D3DXCOLOR(0.5f, 0.4843f, 0.3f, 1.0f);
	//light.specular = D3DXCOLOR(0.5f, 0.5f, 0.5f, 1.0f);
	//m_ParalleLightEffect.SetLight(light);
	m_ParalleLightEffect.SetLight(m_Light);

	//set the node info and draw it's meshes,using queue to visit all the node
	std::queue<SceneNode*> nodeQueue;
	SceneNode* pNode;

	nodeQueue.push(m_pSceneRootNode);
	while(!nodeQueue.empty())
	{
		//get the top node in the queue
		pNode = nodeQueue.front();
		nodeQueue.pop();

		//draw the node
		if(pNode->HasMesh())
		{
			DrawSceneNode(pNode);
		}

		//add the node's children into the queue
		std::list<SceneNode*>::const_iterator iter;
		for(iter=pNode->m_lstChildren.begin() ;iter!=pNode->m_lstChildren.end() ;++iter)
			nodeQueue.push(*iter);
	}

}

void SceneManager::UpdateNodeWorldTransform( SceneNode* parent )
{
	std::list<SceneNode*>::iterator iter;

	for(iter=parent->m_lstChildren.begin() ;iter!=parent->m_lstChildren.end() ;++iter)
	{
		(*iter)->m_matWorldTransform = parent->m_matWorldTransform * (*iter)->m_matTransFromParent;
		UpdateNodeWorldTransform(*iter);
	}
}

void SceneManager::AddObject( SceneNode* objNode )
{
	m_mapObject.insert(make_pair(objNode->m_strSceneNodeName,objNode));
}

SceneNode* SceneManager::GetObjectByName( const std::string& name )
{
	std::map<std::string,SceneNode*>::const_iterator iter;
	iter = m_mapObject.find(name);

	if(iter == m_mapObject.end())
		return NULL;

	return iter->second;
}

void SceneManager::DrawMesh( Mesh* mesh )
{
	//set the buffer
	static UINT stride = sizeof(MeshVertex);
	static UINT offset = 0;
	ID3D10Buffer* pVertexBuffer = mesh->GetVertexBuffer();
	m_pDevice->IASetVertexBuffers(0, 1, &pVertexBuffer, &stride, &offset);
	m_pDevice->IASetIndexBuffer(mesh->GetIndexBuffer(), DXGI_FORMAT_R32_UINT, 0);

	//set the material property
	MaterialInfo* pInfo = mesh->GetMaterialInfo();
	Material* material = ResourcesManager::Instance()->GetMaterialByInfo(pInfo);

	m_ParalleLightEffect.SetDiffuse(material->GetDiffuse());
	m_ParalleLightEffect.SetSpecular(material->GetSpecular());

	D3D10_TECHNIQUE_DESC techDesc;
	m_ParalleLightEffect.GetEffectTech()->GetDesc(&techDesc);
	for(UINT i=0 ;i<techDesc.Passes ;++i)
	{
		m_ParalleLightEffect.GetEffectTech()->GetPassByIndex(i)->Apply(0);

		m_pDevice->DrawIndexed(mesh->GetNumFaces()*3,0,0);
	}
}

inline void SceneManager::DrawSceneNode( SceneNode* node )
{


	std::list<int>::const_iterator iter;
	for(iter=node->m_lstMeshs.begin() ;iter!=node->m_lstMeshs.end() ;++iter)
	{
		//set the world matrix
		m_ParalleLightEffect.SetWorldMatrix(node->GetWorldMatrix());

		//set the projection matrix
		m_ParalleLightEffect.SetProjMatrix(GlobalMessage::Instance()->GetProjMatrix());

		//set the view matrix
		Camera* pCamera = static_cast<Camera*>(GetObjectByName("MainCamera"));
		if(pCamera)
			m_ParalleLightEffect.SetViewMatrix(pCamera->GetViewMatrix());
		else Logger::Instance()->LogString("no camera");

		DrawMesh(ResourcesManager::Instance()->GetMeshByID(*iter));
	}
}

SceneNode* SceneManager::GetSceneRootNode()
{
	return m_pSceneRootNode;
}

void SceneManager::SetLight( const Light& _light )
{
	m_Light = _light;
}

bool SceneManager::IntersectWithNode( SceneNode* pNode,Ray ray )
{
	Mesh* pMesh = ResourcesManager::Instance()->GetMeshByID( *(pNode->m_lstMeshs.begin()) );

	//convert ray into the world space
	D3DXMATRIX view = GlobalMessage::Instance()->GetViewMatrix();

	D3DXMATRIX inverseV;
	D3DXMatrixInverse(&inverseV, 0, &view);

	D3DXVec3TransformCoord(&ray.m_vec3Pos,&ray.m_vec3Pos,&inverseV);
	D3DXVec3TransformNormal(&ray.m_vec3Dir,&ray.m_vec3Dir,&inverseV);

	//in the chess app,the chessboard grid's world matrix is identity,so we do nothing
	;
	;

	if(pMesh->Intersect(ray))
		return true;
	return false;
}

bool SceneManager::DestroyNode( SceneNode* pNode )
{
	std::list<SceneNode*>::iterator iterChild;

	for(iterChild=pNode->m_lstChildren.begin(); iterChild!=pNode->m_lstChildren.end(); ++iterChild)
		DestroyNode(*iterChild);
	if(pNode)
	{
		delete pNode;
		pNode = NULL;
	}

	return true;
}
